use crate::{ Ray, Hitable, HitRecord, AABB, Interval };

/// A collection of hitable objects
/// 
#[derive(Default)]
pub struct Hitables {
    pub objects: Vec<Box<dyn Hitable>>,
    pub aabb: AABB,
}

// Safety: Hitables send to ray tracing threads and distribute to N threads
unsafe impl Send for Hitables {}
unsafe impl Sync for Hitables {}

impl Hitables {
    /// add a hitable object to collection
    /// 
    pub fn add(&mut self, hitable: impl Hitable + 'static) {
        self.add_boxed(Box::new(hitable));
    }

    pub fn add_boxed(&mut self, hitable: Box<dyn Hitable>) {
        self.aabb = AABB::from_two_aabb(
            &self.aabb, 
            hitable.bounding_box()
        );
        self.objects.push(hitable);
    }
}

impl Hitable for Hitables {
    fn hit(&self, ray: &Ray, interval: Interval) -> Option<HitRecord> {
        let mut hit_rec = None;
        let mut closest_so_far = interval.max;

        for obj in self.objects.iter() {
            if let Some(rec) = obj.hit(ray, Interval::new(interval.min, closest_so_far)) {
                closest_so_far = rec.t;
                hit_rec = Some(rec);
            }
        }

        hit_rec
    }

    fn bounding_box(&self) -> &AABB {
        &self.aabb
    }
}